package view.viewControllers 
{
	import controller.EventBus;
	import controller.GameViewControllerEvent;
	import controller.NPCModelEvent;
	import controller.ZeldaComponentEvent;
	import controller.ZeldaModelEvent;
	import model.data.InventoryObjectTypes;
	import model.data.MovementDirections;
	import model.data.NPCTypes;
	import model.GameTimerModel;
	import model.NPCFeedback;
	import model.NPCModel;
	import model.ZeldaModel;
	import org.flixel.FlxCamera;
	import org.flixel.FlxG;
	import org.flixel.FlxGroup;
	import org.flixel.FlxObject;
	import org.flixel.FlxSprite;
	import org.flixel.FlxState;
	import org.flixel.FlxText;
	import org.flixel.FlxTilemap;
	import view.NPCFactory;
	import view.viewComponents.Inventory;
	import view.viewComponents.NPC;
	import view.viewComponents.TimerComponent;
	import view.viewComponents.Zelda;
	
	
	import org.flixel.plugin.photonstorm.FlxCollision;
	
	/**
	 * ...
	 * @author Gaz Williams
	 * 
	 * mediates the view for the game screen to the game model
	 * also mediates the ui for the player, eg when the player interacts with an npc
	 * serves as the context for the models which make up the game.
	 * 
	 */
	
	public class GameViewController extends FlxState 
	{
		
		[Embed(source = "../../../embedded/background0.txt", mimeType = "application/octet-stream")] 
		private var BackgroundMapOne:Class;
		
		[Embed(source = "../../../embedded/background1.txt", mimeType = "application/octet-stream")]
		private var BackgroundMapTwo:Class;
		
		[Embed(source = "../../../embedded/background2.txt", mimeType = "application/octet-stream")] 
		private var BackgroundMapThree:Class;
		
		[Embed(source = "../../../embedded/guideLayer.txt", mimeType = "application/octet-stream")] 
		private var GuideMap:Class;
		
		[Embed(source = '../../../embedded/tileset.png')]
		private var Tiles:Class;
		
		private const WIDTH_IN_TILES:uint 	= 20;
		private const HEIGHT_IN_TILES:uint 	= 16;
		
		private var bus:EventBus;
		
		private var npcGroup:FlxGroup = new FlxGroup();
		private var player:Zelda;
		private var guideMap:FlxTilemap;
		
		private var mText:FlxText;
		private var mPopover:FlxSprite;
		
		private var tModel:GameTimerModel;
		private var zModel:ZeldaModel;
		private var nModels:Vector.<NPCModel> = new Vector.<NPCModel>(9);
		
		private var npcLastInteractedWith:NPCTypes = null;
		
		private var IS_POPOVER:Boolean = false;
		private var FLAG_INTERACTION:Boolean = false;
		
		//set up the context
		override public function create():void 
		{
			if (CONFIG::debug)
			{
				FlxG.debug = true;
				FlxG.visualDebug = true;
			}
				
			bus = EventBus.getInstance(); //ahead of creating objects which may dispatch to this...
			bus.addEventListener(ZeldaModelEvent.POSITION_UPDATED,   handleZeldaModelPosition);
			bus.addEventListener(ZeldaComponentEvent.MOVE_COMPLETED, handleMoveCompleted);
			bus.addEventListener(NPCModelEvent.RIGHT_ITEM, 			 handleRightItemGivenToNPC);
			bus.addEventListener(NPCModelEvent.WRONG_ITEM, 			 handleWrongItemGivenToNPC);
			
			//add maps
			var bg0:FlxTilemap = new FlxTilemap();
			bg0.solid = false;
			add(bg0.loadMap(new BackgroundMapOne(), Tiles, 32, 32, 0, 1));
			
			var bg1:FlxTilemap = new FlxTilemap();
			add(bg1.loadMap(new BackgroundMapTwo(), Tiles, 32, 32, 0, 1));
			bg1.solid = false;
			
			var bg2:FlxTilemap = new FlxTilemap();
			add(bg2.loadMap(new BackgroundMapThree(), Tiles, 32, 32, 0, 1));
			bg2.solid = false;
			
			//interactive map
			guideMap = new FlxTilemap();
			guideMap.solid = false;
			
			//if(CONFIG::release) guideMap.visible = false;
			
			add(guideMap.loadMap(new GuideMap(), Tiles, 32, 32, 0, 1)); //FLEVEL seems to be '1' based
			
			//add npc's at preset locations
			addNPCs();
		
			var tileSize:uint = FlxG.width / WIDTH_IN_TILES * 2;
			player = new Zelda(tileSize, 2, 2);
			add(player);
			
			//camera follows player
			FlxG.camera.setBounds(0, 0, guideMap.width, guideMap.height);
			FlxG.camera.follow(player, FlxCamera.STYLE_TOPDOWN);
			
			//create all the necessary models
			tModel = new GameTimerModel(30, 5, 5);
			zModel = new ZeldaModel(WIDTH_IN_TILES, HEIGHT_IN_TILES, 2, 2);
			
			//let everything know we are ready to go...
			//bus.dispatchEvent(new GameViewControllerEvent(GameViewControllerEvent.VIEW_INITIALISED));
			
			createHUDItems();
			
			//add(new TimerComponent());
		}
			
		override public function destroy():void 
		{
			bus.removeEventListener(ZeldaModelEvent.POSITION_UPDATED, 	handleZeldaModelPosition);
			bus.removeEventListener(ZeldaComponentEvent.MOVE_COMPLETED, handleMoveCompleted);
			bus.removeEventListener(NPCModelEvent.RIGHT_ITEM, 			handleRightItemGivenToNPC);
			bus.removeEventListener(NPCModelEvent.WRONG_ITEM, 			handleWrongItemGivenToNPC);
			
			for each (var _model:NPCModel in nModels)
			{
				_model.dispose();
				_model = null;
			}
			
			tModel.dispose();
			tModel = null;
			
			zModel.dispose();
			zModel = null;
			
			super.destroy();
		}
		
		private function createHUDItems():void 
		{
			var inv:Inventory = new Inventory(FlxG.width * 0.5 - 16, 10);
			inv.scrollFactor.x = 0;
			inv.scrollFactor.y = 0;
			add(inv);
			
			mPopover = new FlxSprite();
			mPopover.makeGraphic(FlxG.width, FlxG.height, 0xCC000000);
			mPopover.scrollFactor.x = 0;
			mPopover.scrollFactor.y = 0;
			add(mPopover);
			
			mText = new FlxText(0, 0, 200);
			mText.text = "hello world";
			mText.scrollFactor.x = 0;
			mText.scrollFactor.y = 0;
			add(mText);
			
			hidePopover();	
		}
		
		private function handleMoveCompleted(e:ZeldaComponentEvent):void 
		{
			if (false == FLAG_INTERACTION) return;
			
			var data:Object = { npc:npcLastInteractedWith, playerItem:zModel.currentlyHeld };
			bus.dispatchEvent(new GameViewControllerEvent(GameViewControllerEvent.PLAYER_INTERACTED_WITH_NPC, data));
			
			//free the flag
			FLAG_INTERACTION = false;
		}
		
		private function addNPCs():void 
		{
			//at preset locations
			for (var type:int = 0; type < 9; type++)
			{
				nModels.push(new NPCModel(new NPCTypes(type)));
				npcGroup.add(NPCFactory.createFromType(new NPCTypes(type)));
				add(npcGroup);
			}
			
		}
		
		private function handleZeldaModelPosition(e:ZeldaModelEvent):void 
		{
			//trace(" gvc receiving model update");
			
			var column:int 	= int(e.data.column);
			var row:int 	= int(e.data.row);
			
			var guideValue:uint = guideMap.getTile(column, row);
			
			if (guideValue > 0)
			{
				//trace(" gvc dispatching player hit guide:", column, row);
				var data:MovementDirections = new MovementDirections(guideValue)
				bus.dispatchEvent(new GameViewControllerEvent(GameViewControllerEvent.PLAYER_HIT_GUIDE_MARKER, data));
			}
		}
		
		override public function update():void 
		{
			
			//check for collisions between the player and npc
			if (false == IS_POPOVER)
			{
				checkNPCCollisions();
			}
			else
			{	
				if (FlxG.keys.SPACE)
				{
					hidePopover();
					bus.dispatchEvent(new GameViewControllerEvent(GameViewControllerEvent.PLAYER_INTERACTION_COMPLETED, null));
				}
			}
			
			//tell the world the world that an update occurred
			bus.dispatchEvent(new GameViewControllerEvent(GameViewControllerEvent.UPDATE, null));
			
			super.update();
		}
		
		private function checkNPCCollisions():void 
		{
			//trace("check npc collisions");
			
			for each (var npc:NPC in npcGroup.members)
			{
				var npcCollision:Boolean = FlxCollision.pixelPerfectCheck(player, npc);
				if (npcCollision)
				{
					bus.dispatchEvent(new GameViewControllerEvent(GameViewControllerEvent.PLAYER_HIT_NPC, null));
					
					var repeatInteraction:Boolean = npcLastInteractedWith && npcLastInteractedWith.type == npc.type.type;
					if (FlxG.keys.SPACE && false == repeatInteraction)
					{
						npcLastInteractedWith = npc.type;
						FLAG_INTERACTION = true;
					}
				}
			}
		}
		
		private function handleRightItemGivenToNPC(e:NPCModelEvent):void 
		{
			var item:InventoryObjectTypes = e.forInventoryItem;
			
			var feedback:String = NPCFeedback.getText(item, true);
			
			showPopoverWithText(feedback);
		}
		
		private function handleWrongItemGivenToNPC(e:NPCModelEvent):void 
		{
			var item:InventoryObjectTypes = e.forInventoryItem;
			
			var feedback:String = NPCFeedback.getText(item, false);
			
			showPopoverWithText(feedback);
		}
		
		private function showPopoverWithText(text:String):void 
		{
			mPopover.visible 	= true;
			mText.visible 		= true;
			mText.text = text;
			
			centerTextField();
			
			IS_POPOVER = true;
		}
		
		private function hidePopover():void 
		{
			mPopover.visible 	= false;
			mText.visible 		= false;
			
			IS_POPOVER = false;
		}
		
		private function centerTextField():void 
		{
			mText.x = (FlxG.width - mText.width) * 0.5;
			mText.y = 20;
		}
		
	}

}